package com.kyivmohyla.kma2.client.presenter.manager;

import com.google.gwt.cell.client.NumberCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.DatePicker;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.kyivmohyla.kma2.client.UserService;
import com.kyivmohyla.kma2.client.events.manager.ManagerClosedOrdersOpenEvent;
import com.kyivmohyla.kma2.client.events.manager.ManagerEditOrderEvent;
import com.kyivmohyla.kma2.client.events.refbook.TreeGridOpenEvent;
import com.kyivmohyla.kma2.client.gin.Injector;
import com.kyivmohyla.kma2.client.presenter.Presenter;
import com.kyivmohyla.kma2.client.utils.Dialog;
import com.kyivmohyla.kma2.client.view.common.OrderCellTableWidget;
import com.kyivmohyla.kma2.shared.model.UIOrder;
import com.kyivmohyla.kma2.shared.model.UIStaff;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class ManagerMainViewPresenter implements Presenter {

    private final static DateTimeFormat dateFormat = DateTimeFormat.getFormat("dd.MM.yyyy");
    private final static DateTimeFormat timeFormat = DateTimeFormat.getFormat("HH:mm");
    private final Display display;
    private final Injector injector = Injector.INSTANCE;
    private MultiSelectionModel<UIOrder> multiSelectionModel;
    private CellTable<UIOrder> orderCellTable;

    public ManagerMainViewPresenter(UIStaff manager) {
        this.display = injector.getManagerMainView();
        this.orderCellTable = (CellTable) this.display.getOrderCellTable().getOrderCellTable();
        createTableStructure();
        String formattedDate = dateFormat.format(new Date());
        bind();
        UserService.App.getInstance().getOrdersByStateAndDate(new int[]{2, 5}, formattedDate, new AsyncCallback<List<UIOrder>>() {
            @Override
            public void onFailure(Throwable throwable) {
                Dialog.getInstance().createDialogBoxWithMessage("Error", throwable.getMessage());
            }

            @Override
            public void onSuccess(List<UIOrder> uiOrders) {
                ListDataProvider<UIOrder> dataProvider = new ListDataProvider<UIOrder>(uiOrders);
                dataProvider.addDataDisplay(orderCellTable);
            }
        });
    }

    public ManagerMainViewPresenter(UIStaff manager, Date date) {
        this.display = injector.getManagerMainView();
        this.orderCellTable = (CellTable) this.display.getOrderCellTable().getOrderCellTable();
        createTableStructure();
        String formattedDate = dateFormat.format(date);
        bind();
        ((DatePicker) this.display.getDateBox()).setValue(date, false);
        UserService.App.getInstance().getOrdersByStateAndDate(new int[]{2, 5}, formattedDate, new AsyncCallback<List<UIOrder>>() {
            @Override
            public void onFailure(Throwable throwable) {
                Dialog.getInstance().createDialogBoxWithMessage("Error", throwable.getMessage());
            }

            @Override
            public void onSuccess(List<UIOrder> uiOrders) {
                ListDataProvider<UIOrder> dataProvider = new ListDataProvider<UIOrder>(uiOrders);
                dataProvider.addDataDisplay(orderCellTable);
            }
        });
    }

    private void createTableStructure() {
        multiSelectionModel = new MultiSelectionModel<UIOrder>();
        orderCellTable.setSelectionModel(multiSelectionModel);

        orderCellTable.addDomHandler(new DoubleClickHandler() {
            @Override
            public void onDoubleClick(DoubleClickEvent doubleClickEvent) {
                injector.getEventBus().fireEvent(new ManagerEditOrderEvent((UIOrder) multiSelectionModel.getSelectedSet().toArray()[0]));
            }
        }, DoubleClickEvent.getType());

        final DateTimeFormat dateFormat = DateTimeFormat.getFormat("dd.MM.yyyy HH:mm");
        orderCellTable.addColumn(new Column<UIOrder, Number>(new NumberCell()) {
            @Override
            public Number getValue(UIOrder object) {
                return object.getOrderId();
            }
        }, "ID");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                return object.getStateName();
            }
        }, "State");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                if (object.getCreateDate() != null)
                    return dateFormat.format(object.getCreateDate());
                return "#######";
            }
        }, "Creation Date");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                return object.getCustomerName();
            }
        }, "Customer");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                if (object.getDesStartDate() != null)
                    return dateFormat.format(object.getDesStartDate());
                return "#######";
            }
        }, "Desirable Start");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                if (object.getDesEndDate() != null)
                    return dateFormat.format(object.getDesEndDate());
                return "#######";
            }
        }, "Desirable End");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                return object.getCommentary();
            }
        }, "Commentary");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                if (object.getStartDate() != null)
                    return dateFormat.format(object.getStartDate());
                return "Not Planned Yet";
            }
        }, "Planned Start");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                if (object.getEndDate() != null)
                    return dateFormat.format(object.getEndDate());
                return "Not Planned Yet";
            }
        }, "Planned End");
        orderCellTable.addColumn(new Column<UIOrder, String>(new TextCell()) {
            @Override
            public String getValue(UIOrder object) {
                return object.getEngineerName();
            }
        }, "Engineer");
    }

    private void bind() {
        this.display.getEquipmentButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                doOpenEquipmentReferenceBook();
            }
        });
        this.display.getServiceButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                doOpenServiceReferenceBook();
            }
        });
        this.display.getDateBox().addValueChangeHandler(new ValueChangeHandler<Date>() {
            @Override
            public void onValueChange(ValueChangeEvent<Date> valueChangeEvent) {
                doValueChange(valueChangeEvent.getValue());
            }
        });
        this.display.getEditSelectedOrderButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                doOpenEditOrderWindow();
            }
        });

        this.display.getClosedOrdersButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                doOpenClosedOrders();
            }
        });

        this.display.getValidateButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {

                if (!getOrdersFromTableToValidate().isEmpty()) {
                    UserService.App.getInstance().validateOrderSchedule(getOrdersFromTableToValidate(), new AsyncCallback<Map<UIOrder, Boolean>>() {
                        @Override
                        public void onFailure(Throwable caught) {
                            Dialog.getInstance().createDialogBoxWithMessage("Message", "Something went wrong, here's original error message </br>" + caught.getMessage());
                        }

                        @Override
                        public void onSuccess(Map<UIOrder, Boolean> result) {
                            String successfulPart = "";
                            String failurePart = "";
                            for (UIOrder order : result.keySet()) {
                                if (result.get(order)) {
                                    successfulPart += getOrderInfo(order) + "</br>";
                                } else {
                                    failurePart += getOrderInfo(order) + "</br>";
                                }
                            }
                            String report = (successfulPart.equals("") ? "" : "<b>Such orders can be validated:</b> </br></br>" + successfulPart + "</br>") +
                                    (failurePart.equals("") ? "" : "<b>Such orders scheduled for the same day and collide, therefore can not be validated:</b> </br></br>" + failurePart + "</br>")
                                    + "</br><i><u>Note, that system validates orders in \"In Progress\" and \"Accessed\" states</br> that are scheduled and have engineer assigned</u></i></br>" + "</br><b><center>End of Report</center></b>";
                            Dialog.getInstance().createDialogBoxWithMessage("Report", report);
                        }
                    });
                } else {
                    Dialog.getInstance().createDialogBoxWithMessage("Message", "Nothing to validate</br></br><u><i>Note that system validates orders in \"In Progress\" and \"Accessed\" states that are scheduled and have engineer assigned</i></u>");
                }

            }
        });
        this.display.getAccessButton().addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                if (!getOrdersFromTableToValidate().isEmpty()) {
                    UserService.App.getInstance().validateOrderSchedule(getOrdersFromTableToValidate(), new AsyncCallback<Map<UIOrder, Boolean>>() {
                        @Override
                        public void onFailure(Throwable caught) {
                            Dialog.getInstance().createDialogBoxWithMessage("Message", "Something went wrong, here's original error message </br>" + caught.getMessage());
                        }

                        @Override
                        public void onSuccess(Map<UIOrder, Boolean> result) {
                            String successfulPart = "";
                            String failurePart = "";
                            //orders that will be accessed
                            List<UIOrder> successfulOrders = new ArrayList<UIOrder>();
                            for (UIOrder order : result.keySet())
                                if (result.get(order)) {
                                    successfulPart += getOrderInfo(order) + "</br>";
                                    order.setStateId(3);
                                    //email sending
//                                    UserService.App.getInstance().getCustomerById(order.getCustomerId(),new AsyncCallback<UICustomer>() {
//                                        @Override
//                                        public void onFailure(Throwable caught) {
//                                            Dialog.getInstance().createDialogBoxWithMessage("Error","Something went wrong</br>"+caught.getMessage());
//                                        }
//
//                                        @Override
//                                        public void onSuccess(UICustomer result) {
////                                            UserService.App.getInstance().sendMail(result.getEmail(),"Order Schedule","Hi, your");
//                                        }
//                                    });
                                    successfulOrders.add(order);
                                } else if (getOrdersFromTableToValidate().contains(order))
                                    failurePart += getOrderInfo(order) + "</br>";
                            String message = (successfulPart.equals("") ? "" : "<b>Such orders will be accessed to correspondent engineers:</b></br>" + successfulPart) +
                                    (failurePart.equals("") ? "" : "<b>Such orders can not be accessed, because of schedule collision </b></br>" + failurePart) + "</br><center>End of Report</center>";
                            Dialog.getInstance().createDialogBoxWithMessage("Report", message);
                            //now we have to assign orders
                            if (!successfulOrders.isEmpty())
                                UserService.App.getInstance().updateOrderList(successfulOrders, new AsyncCallback<Void>() {
                                    @Override
                                    public void onFailure(Throwable caught) {
                                        Dialog.getInstance().createDialogBoxWithMessage("Message", "Something went wrong, maybe no connection to the database.</br>Anyway, here is the original message:</br>" + caught.getMessage());
                                    }

                                    @Override
                                    public void onSuccess(Void result) {
                                        doValueChange(((DatePicker) display.getDateBox()).getValue());
                                    }
                                });
                        }
                    });
                } else {
                    Dialog.getInstance().createDialogBoxWithMessage("Message", "Selected orders can not be accessed due to absence of schedule and(or) assigned Engineer </br> or they are in \"Need More Info\" state.");
                }
            }
        });
    }

    //returns either selected orders or all visible orders
    private List<UIOrder> getOrdersFromTableToValidate() {
        List<UIOrder> listToValidate = new ArrayList<UIOrder>();
        if (multiSelectionModel.getSelectedSet().isEmpty()) {
            for (UIOrder order : orderCellTable.getVisibleItems())
                if (order.getStartDate() != null && order.getEndDate() != null && order.getEngineerId() != 0 && (order.getStateId() == 2 || order.getStateId() == 3))
                    listToValidate.add(order);
        } else {
            for (UIOrder order : multiSelectionModel.getSelectedSet())
                if (order.getStartDate() != null && order.getEndDate() != null && order.getEngineerId() != 0 && (order.getStateId() == 2 || order.getStateId() == 3))
                    listToValidate.add(order);
        }
        return listToValidate;
    }

    //nice neat output
    private String getOrderInfo(UIOrder order) {
        return "id  " + order.getOrderId() + ",(" + order.getStateName() + (order.getEngineerName().equals("") ? "" : ", " + order.getEngineerName()) + "), " +
                dateFormat.format(order.getStartDate()) + ", " +
                timeFormat.format(order.getStartDate()) + " - " +
                timeFormat.format(order.getEndDate());
    }
//
//    private String composeEmail(UIOrder order){
//        String date =   dateFormat.format(order.getStartDate()) + ", " +
//                        timeFormat.format(order.getStartDate()) + " - " +
//                        timeFormat.format(order.getEndDate());
//        String res = "";
//        return res;
//    }

    private void doOpenEditOrderWindow() {
        if (multiSelectionModel.getSelectedSet().size() == 0 || multiSelectionModel.getSelectedSet().size() > 1) {
            Dialog.getInstance().createDialogBoxWithMessage("Please,", "Select one order first.");
        } else {
            injector.getEventBus().fireEvent(new ManagerEditOrderEvent((UIOrder) multiSelectionModel.getSelectedSet().toArray()[0]));
        }
    }

    private void doOpenServiceReferenceBook() {
        injector.getEventBus().fireEvent(new TreeGridOpenEvent("Service"));
    }

    private void doOpenEquipmentReferenceBook() {
        injector.getEventBus().fireEvent(new TreeGridOpenEvent("Equipment"));
    }

    private void doOpenClosedOrders() {
        injector.getEventBus().fireEvent(new ManagerClosedOrdersOpenEvent());
    }

    private void doValueChange(Date date) {
        for (UIOrder order : orderCellTable.getVisibleItems()) {
            multiSelectionModel.setSelected(order, false);
        }
        String formattedDate = dateFormat.format(date);
        UserService.App.getInstance().getOrdersByStateAndDate(new int[]{2, 5}, formattedDate, new AsyncCallback<List<UIOrder>>() {
            @Override
            public void onFailure(Throwable throwable) {
                Dialog.getInstance().createDialogBoxWithMessage("Error", throwable.getMessage());
            }

            @Override
            public void onSuccess(List<UIOrder> uiOrders) {
                ListDataProvider<UIOrder> dataProvider = new ListDataProvider<UIOrder>(uiOrders);
                dataProvider.addDataDisplay(display.getOrderCellTable().getOrderCellTable());
            }
        });
    }

    @Override
    public void go(HasWidgets container) {
        container.clear();
        container.add(display.asWidget());
    }

    public interface Display {

        HasClickHandlers getEquipmentButton();

        HasClickHandlers getServiceButton();

        HasClickHandlers getValidateButton();

        HasClickHandlers getAccessButton();

        HasClickHandlers getClosedOrdersButton();

        HasClickHandlers getEditSelectedOrderButton();

        HasValueChangeHandlers getDateBox();

        OrderCellTableWidget getOrderCellTable();

        Widget asWidget();
    }
}
